/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <journal/app/keys.h>
#include <journal/views/scene_references.h>
#include <journal/app/constants.h>

namespace journal { namespace view {

#if defined(_M_IX86)
const b::wchar* scene_references::c_platform = L"x86";
#elif defined(_M_X64)
const b::wchar* scene_references::c_platform = L"x64";
#else
const b::wchar* scene_references::c_platform = L"??";
#endif

#if defined(_DEBUG)
bool c_renderDisclaimer = true;
const b::wchar* scene_references::c_config = L"DEBUG";
#else
bool c_renderDisclaimer = false;
const b::wchar* scene_references::c_config = L"RELEASE";
#endif

scene_references::scene_references(
    const std::shared_ptr<a::iinput_capture>& inputCapture,
    scene_state* sceneState
    ) : 
    _state(state::idle), 
    _inputCapture(inputCapture), 
    _sceneState(sceneState), 
    _visible(true),
    _down(false),
    _renderDisclaimer(c_renderDisclaimer)
{
    std::shared_ptr<g::iasset_manager> assetManager = constants::graphics()->getAssetManager();

    _font = assetManager->getOrCreateFont(
        constants::graphics(), 
        constants::references::fontName(), 
        constants::references::fontSize(),
        false // right to left
        );

    _debugString.resize(max_debug_string);

    this->updateDebugString();
    constants::debugConsole()->getRoot()->addStringVar(
        L"camera",
        _debugString.data(),
        0
        );
}

void scene_references::onRender()
{
    if (_renderDisclaimer)
    {
        this->renderDisclaimer();
    }

    if (_visible)
    {
        this->updateDebugString();
        if (constants::references::showAxes())
        {
            this->renderAxes();
            this->renderFloor();
            this->renderFocus();
            this->renderLight();
        }
            
        if (constants::references::showFPS())
        {
            constants::uiService()->setTextVisibility(true);
            this->renderFPS();
        }
        else
        {
            constants::uiService()->setTextVisibility(false);
        }
    }
}

void scene_references::renderDisclaimer()
{
    std::shared_ptr<g::iengine> graphics = constants::graphics();

    boost::wformat fmt = boost::wformat(L"Austin build %s %s, built on %s.") %
        c_config % c_platform %
        __DATE__;

    std::wstring ws = fmt.str();

    g::viewport vp;
    graphics->getViewport(&vp);

    // measure the text's rect
    m::rectangle textRect;
    _font->measureText(
        ws.c_str(),
        g::horizontal_alignment::right,
        g::vertical_alignment::top,
        false,
        &textRect
        );

    textRect.top += 12;
    textRect.bottom += 12;
    textRect.left += vp.width - 10;
    textRect.right += vp.width - 10;

    // draw backdrop rectangle
    m::rectangle backdropRect(textRect);
    backdropRect.top -= 3;
    backdropRect.bottom += 1;
    backdropRect.left -= 4;
    backdropRect.right += 2;
            
    std::shared_ptr<g::ieffect2> effect;
    effect = graphics->getStandardEffect(g::standard_effect::screenColored);

    effect->begin(graphics);
    graphics->setAlphaBlend(g::enabled_state::enabled);
    graphics->setZBuffer(g::enabled_state::disabled);
    graphics->setCulling(g::cull_state::none);

    graphics->beginScreenSpace(g::primitive_type::triangleStrip);

    graphics->setColor(g::packColor(128, 255, 0, 0));

    m::point4d pos1(backdropRect.left-0.5f, backdropRect.top-0.5f, 1.0f);
    graphics->setScreenSpaceVertex(pos1);

    m::point4d pos2(backdropRect.right-0.5f, backdropRect.top-0.5f, 1.0f);
    graphics->setScreenSpaceVertex(pos2);

    graphics->setColor(g::packColor(128, 255, 0, 0));

    m::point4d pos4(backdropRect.left-0.5f, backdropRect.bottom-0.5f, 1.0f);
    graphics->setScreenSpaceVertex(pos4);

    m::point4d pos3(backdropRect.right-0.5f, backdropRect.bottom-0.5f, 1.0f);
    graphics->setScreenSpaceVertex(pos3);

    graphics->end();

    effect->end(graphics);

    // draw the fps text

    static const g::packed_color textColor = g::packColor(255,255,255,255);
    textRect.right -= 2;
    textRect.top -= 2;
    _font->renderText(
        graphics,
        ws.c_str(),
        textColor,
        g::horizontal_alignment::left,
        g::vertical_alignment::top,
        &textRect
        );
}


//#define USE_XAML_TEXT 1

void scene_references::renderFPS()
{
    std::shared_ptr<g::iengine> graphics = constants::graphics();

    b::float32 count = 0;
    b::float32 value = 0;
    constants::performanceService()->get(performance_event::renderFrame, &count, &value);

#ifdef USE_XAML_TEXT
    if (count <= 0)
    {
        constants::uiService()->setText(L"fps: --, ms: --");
    }
    else
    {
        boost::wformat fmt = boost::wformat(L"fps: %0.2f, ms: %0.2f") % count % (value/count);

        std::wstring ws = fmt.str();
        constants::uiService()->setText(ws.c_str());
    }
#else // render FPS using dx
    boost::wformat fmt = boost::wformat(L"FPS: %0.2f") % constants::renderService()->getFramesPerSecond();
    std::wstring ws = fmt.str();

    g::viewport vp;
    graphics->getViewport(&vp);

    // measure the text's rect
    m::rectangle textRect;
    _font->measureText(
        ws.c_str(),
        g::horizontal_alignment::right,
        g::vertical_alignment::top,
        false,
        &textRect
        );

    textRect.top += 72;
    textRect.bottom += 72;
    textRect.left += vp.width - 10;
    textRect.right += vp.width - 10;

    // draw backdrop rectangle
    m::rectangle backdropRect(textRect);
    backdropRect.top -= 3;
    backdropRect.bottom += 1;
    backdropRect.left -= 4;
    backdropRect.right += 2;
            
    std::shared_ptr<g::ieffect2> effect;
    effect = graphics->getStandardEffect(g::standard_effect::screenColored);

    effect->begin(graphics);
    graphics->setAlphaBlend(g::enabled_state::enabled);
    graphics->setZBuffer(g::enabled_state::disabled);
    graphics->setCulling(g::cull_state::none);

    graphics->beginScreenSpace(g::primitive_type::triangleStrip);

    graphics->setColor(g::packColor(128, 0, 0, 0));

    m::point4d pos1(backdropRect.left-0.5f, backdropRect.top-0.5f, 1.0f);
    graphics->setScreenSpaceVertex(pos1);

    m::point4d pos2(backdropRect.right-0.5f, backdropRect.top-0.5f, 1.0f);
    graphics->setScreenSpaceVertex(pos2);

    graphics->setColor(g::packColor(128, 0, 0, 0));

    m::point4d pos4(backdropRect.left-0.5f, backdropRect.bottom-0.5f, 1.0f);
    graphics->setScreenSpaceVertex(pos4);

    m::point4d pos3(backdropRect.right-0.5f, backdropRect.bottom-0.5f, 1.0f);
    graphics->setScreenSpaceVertex(pos3);

    graphics->end();

    effect->end(graphics);

    // draw the fps text

    static const g::packed_color textColor = g::packColor(255,198,198,198);
    textRect.right -= 2;
    textRect.top -= 2;
    _font->renderText(
        graphics,
        ws.c_str(),
        textColor,
        g::horizontal_alignment::left,
        g::vertical_alignment::top,
        &textRect
        );
#endif
}

void scene_references::renderFloor()
{
    b::int32 tileCount = constants::references::floorTileCount();
    b::float32 tileGap = constants::references::floorTileSize();

    std::shared_ptr<g::iengine> graphics = constants::graphics();

    b::float32 depth = tileGap * tileCount;

    graphics->setTexture(0, std::shared_ptr<g::itexture>());

    std::shared_ptr<g::ieffect2> effect;
    effect = graphics->getStandardEffect(g::standard_effect::colored);
 
    effect->begin(graphics);

    graphics->setZBuffer(g::enabled_state::enabled);
    graphics->setZCompare(g::compare_state::lessEqual);

    // Render floor
    graphics->begin(g::primitive_type::lineList);

    g::packed_color color = g::packColor(64, 255, 255 ,255);

    b::float32 halfDepth = depth / 2;

    m::point3d vec1(halfDepth * -1, halfDepth * -1, 0);
    m::point3d vec2(halfDepth * 1, halfDepth * -1, 0);

    m::point3d vec3(halfDepth * -1, halfDepth * -1, 0);
    m::point3d vec4(halfDepth * -1, halfDepth * 1, 0);

    graphics->setColor(color);

    for (b::int32 i = 0; i < tileCount; i++)
    {
        graphics->setVertex(vec1);
        graphics->setVertex(vec2);

        vec1.y += tileGap;
        vec2.y += tileGap;

        graphics->setVertex(vec3);
        graphics->setVertex(vec4);

        vec3.x += tileGap;
        vec4.x += tileGap;
    }
    
    graphics->end();
    
    m::point3d origin;
    b::float32 scale = 2.0f;
    m::point3d xVec(scale, 0, 0);
    m::point3d yVec(0, scale, 0);
    m::point3d zVec(0, 0, scale);
    static const g::packed_color xColor = g::packColor(64,255,64,64);
    static const g::packed_color yColor = g::packColor(64,64,255,64);
    static const g::packed_color zColor = g::packColor(64,64,64,255);

    effect->begin(graphics);

    // Render origin
    graphics->setZBuffer(g::enabled_state::disabled);
    graphics->setZCompare(g::compare_state::never);

    graphics->begin(g::primitive_type::lineList);

    for (b::int32 i = 0; i < tileCount; i++)
    {
        graphics->setColor(xColor);
        graphics->setVertex(origin);
        graphics->setVertex(xVec);

        graphics->setColor(yColor);
        graphics->setVertex(origin);
        graphics->setVertex(yVec);

        graphics->setColor(zColor);
        graphics->setVertex(origin);
        graphics->setVertex(zVec);
    }
    
    graphics->end();
    
    effect->end(graphics);
}

void scene_references::renderLight()
{
    std::shared_ptr<g::iengine> graphics = constants::graphics();

    graphics->setTexture(0, std::shared_ptr<g::itexture>());

    std::shared_ptr<g::ieffect2> effect;
    effect = graphics->getStandardEffect(g::standard_effect::colored);
 
    effect->begin(graphics);

    graphics->setZBuffer(g::enabled_state::disabled);
    graphics->setCulling(g::cull_state::none);
    graphics->setZCompare(g::compare_state::lessEqual);

    // Render floor
    graphics->begin(g::primitive_type::lineList);

    g::packed_color color = g::packColor(190, 0, 0, 0);

    const m::point3d& lightPosition = _sceneState->getLight()->camera()->position();
    const m::vector3d& lightDirection = _sceneState->getLight()->camera()->direction();

    m::vector3d vec1(lightPosition);
    m::point3d vec2(lightPosition);
    vec2.offsetBy(lightDirection);
        
    graphics->setColor(color);

    graphics->setVertex(vec1);
    graphics->setVertex(vec2);

    graphics->end();

    // draw label
    static const g::packed_color black = g::packColor(255,0,0,0);
    m::point3d worldPoint;
    m::point2d pixelPoint;
    m::rectangle pixelRect;
    b::float32 z;

    m::point3d origin;
    m::matrix44 world;
    m::matrix44 camCopy = _sceneState->getCamera()->matrix();

    // Note, JORGEPER: this is a bit trick, we are trying to render the coordinate system axes
    // as if the camera was placed in the center of the "real" coordinate system (0,0,0)
    // so we take the camera to the center, then get the "fake" coordinate system's vectors and render them
    // using an orthographic projection (with some offsets/scales to place them at the bottom-right corner of the screen)
    // The trick is that it's setting the camera to identity and having the world be rotated as the inverse (transpose)
    // of the original camera.  Got this code from digit.
    camCopy.setTranslation(origin);
    camCopy.getTranspose(&world);

    std::shared_ptr<g::iscene_transformer> transformer = graphics->snapshotCurrentSceneTransforms();

    world.apply(_sceneState->getLight()->camera()->position(), &worldPoint);
    pixelPoint = transformer->worldSpaceToPixel(_sceneState->getLight()->camera()->position(), &z);

    static const g::packed_color xColor = g::packColor(255,255,0);

    pixelRect.set(pixelPoint.x+1, pixelPoint.y+1, pixelPoint.x+1, pixelPoint.y+1);
    _font->renderText(
        graphics,
        L"LIGHT",
        black,
        g::horizontal_alignment::center,
        g::vertical_alignment::center,
        &pixelRect
        );

    pixelRect.set(pixelPoint.x, pixelPoint.y, pixelPoint.x, pixelPoint.y);
    _font->renderText(
        graphics,
        L"LIGHT",
        xColor,
        g::horizontal_alignment::center,
        g::vertical_alignment::center,
        &pixelRect
        );

    effect->end(graphics);
}

void scene_references::renderFocus()
{
    std::shared_ptr<g::iengine> graphics = constants::graphics();

    graphics->setTexture(0, std::shared_ptr<g::itexture>());

    std::shared_ptr<g::ieffect2> effect;
    effect = graphics->getStandardEffect(g::standard_effect::colored);
    
    effect->begin(graphics);

    graphics->setAlphaBlend(g::enabled_state::enabled);
    graphics->setZBuffer(g::enabled_state::enabled);
    graphics->setZCompare(g::compare_state::less);

    graphics->begin(g::primitive_type::lineList);

    g::packed_color color = g::packColor(255, 0, 255 ,255);

    // top-left, top-right, bottom-right, bottom-left
    m::point3d vec1(
        _sceneState->getCamera()->offsetXY().x - _sceneState->getCamera()->focus().x / 2.f, 
        _sceneState->getCamera()->offsetXY().y + _sceneState->getCamera()->focus().y / 2.f, 
        0.01f
        );
    m::point3d vec2(
        _sceneState->getCamera()->offsetXY().x + _sceneState->getCamera()->focus().x / 2.f, 
        _sceneState->getCamera()->offsetXY().y + _sceneState->getCamera()->focus().y / 2.f, 
        0.01f
        );
    m::point3d vec3(
        _sceneState->getCamera()->offsetXY().x + _sceneState->getCamera()->focus().x / 2.f, 
        _sceneState->getCamera()->offsetXY().y - _sceneState->getCamera()->focus().y / 2.f, 
        0.01f
        );
    m::point3d vec4(
        _sceneState->getCamera()->offsetXY().x - _sceneState->getCamera()->focus().x / 2.f, 
        _sceneState->getCamera()->offsetXY().y - _sceneState->getCamera()->focus().y / 2.f, 
        0.01f
        );

    graphics->setColor(color);

    graphics->setVertex(vec1);
    graphics->setVertex(vec2);
    graphics->setVertex(vec2);
    graphics->setVertex(vec3);
    graphics->setVertex(vec3);
    graphics->setVertex(vec4);
    graphics->setVertex(vec4);
    graphics->setVertex(vec1);

    graphics->end();
   
    effect->end(graphics);
}

void scene_references::renderAxes()
{
    std::shared_ptr<g::iengine> graphics = constants::graphics();

    g::display_params dp;
    ZeroMemory(&dp, sizeof(dp));

    dp = graphics->getDisplayParams();

    b::float32 aspect = 1;

    g::viewport vp;
    graphics->getViewport(&vp);
    b::float32 windowWidth = vp.width;
    b::float32 windowHeight = vp.height;

    m::point3d origin;
    m::matrix44 world;
    m::matrix44 camCopy = _sceneState->getCamera()->matrix();

    // Note, JORGEPER: this is a bit trick, we are trying to render the coordinate system axes
    // as if the camera was placed in the center of the "real" coordinate system (0,0,0)
    // so we take the camera to the center, then get the "fake" coordinate system's vectors and render them
    // using an orthographic projection (with some offsets/scales to place them at the bottom-right corner of the screen)
    // The trick is that it's setting the camera to identity and having the world be rotated as the inverse (transpose)
    // of the original camera.  Got this code from digit.
    camCopy.setTranslation(origin);
    camCopy.getTranspose(&world);
        
    m::matrix44 ident;
    m::point3d transScale;
        
    if (windowWidth > windowHeight)
    {
        aspect = (float32)windowWidth / windowHeight;
        transScale = m::point3d(aspect * 0.9f, -0.85f, 0.5f);
    }
    else
    {
        aspect = (float32)windowHeight / windowWidth;
        transScale = m::point3d(0.85f, aspect * -0.9f, 0.5f);
    }

    m::point3d translation;

    m::vector3d rightVec;
    m::vector3d fwdVec;
    m::vector3d upVec;
    m::matrix44 coordSystemMatrix = graphics->getCoordinateSystemMatrix();
    coordSystemMatrix.getRightVector(&rightVec);
    coordSystemMatrix.getForwardVector(&fwdVec);
    coordSystemMatrix.getUpVector(&upVec);
        
    rightVec.scale(transScale.x);
    upVec.scale(transScale.y);
    fwdVec.scale(transScale.z);
    m::vector3d t;
    rightVec.add(upVec, &t);
    translation = t;
    translation.offsetBy(fwdVec, &translation);

    world.setTranslation(translation);

    graphics->pushWorldMatrix(world, false);
    if (windowWidth > windowHeight)
    {
        graphics->pushOrthographicProjection(-aspect, aspect, 1, -1, 0.1f, 1);
    }
    else
    {
        graphics->pushOrthographicProjection(-1, 1, aspect, -aspect, 0.1f, 1);
    }
    graphics->pushCameraMatrix(ident);

    std::shared_ptr<g::iscene_transformer> transformer = graphics->snapshotCurrentSceneTransforms();

    graphics->setTexture(0, std::shared_ptr<g::itexture>());

    std::shared_ptr<g::ieffect2> effect;
    effect = graphics->getStandardEffect(g::standard_effect::colored);

    effect->setBlendState(L"blend_alpha", nullptr, 0xffffffff);

    const b::float32 axisScale = 0.1f;
    m::point3d xVec(axisScale, 0, 0);
    m::point3d yVec(0, axisScale, 0);
    m::point3d zVec(0, 0, axisScale);

    m::point3d orig(0, 0, 0);

    static const g::packed_color xColor = g::packColor(255,64,64);
    static const g::packed_color yColor = g::packColor(64,255,64);
    static const g::packed_color zColor = g::packColor(64,64,255);

    effect->begin(graphics);
    graphics->setAlphaBlend(g::enabled_state::enabled);
    graphics->setZBuffer(g::enabled_state::disabled);
    graphics->setCulling(g::cull_state::none);

    graphics->begin(g::primitive_type::lineList);
        
    graphics->setColor(xColor);
    graphics->setVertex(orig);
    graphics->setVertex(xVec);

    graphics->setColor(yColor);
    graphics->setVertex(orig);
    graphics->setVertex(yVec);

    graphics->setColor(zColor);
    graphics->setVertex(orig);
    graphics->setVertex(zVec);

    graphics->end();

    effect->end(graphics);

    // draw axis labels
    static const g::packed_color black = g::packColor(255,0,0,0);
    m::point3d worldPoint;
    m::point2d pixelPoint;
    m::rectangle pixelRect;
    b::float32 z;

    // X label (with drop shadow drawn first)
    world.apply(xVec, &worldPoint);
    pixelPoint = transformer->worldSpaceToPixel(worldPoint, &z);

    pixelRect.set(pixelPoint.x, pixelPoint.y, pixelPoint.x, pixelPoint.y);
    _font->renderText(
        graphics,
        L"X",
        xColor,
        g::horizontal_alignment::center,
        g::vertical_alignment::center,
        &pixelRect
        );

    // Y label (with drop shadow drawn first)
    world.apply(yVec, &worldPoint);
    pixelPoint = transformer->worldSpaceToPixel(worldPoint, &z);
        
    pixelRect.set(pixelPoint.x, pixelPoint.y, pixelPoint.x, pixelPoint.y);
    _font->renderText(
        graphics,
        L"Y",
        yColor,
        g::horizontal_alignment::center,
        g::vertical_alignment::center,
        &pixelRect
        );

    // Z label (with drop shadow drawn first)
    world.apply(zVec, &worldPoint);
    pixelPoint = transformer->worldSpaceToPixel(worldPoint, &z);

    pixelRect.set(pixelPoint.x, pixelPoint.y, pixelPoint.x, pixelPoint.y);
    _font->renderText(
        graphics,
        L"Z",
        zColor,
        g::horizontal_alignment::center,
        g::vertical_alignment::center,
        &pixelRect
        );

    graphics->popProjectionMatrix();
    graphics->popWorldMatrix();
    graphics->popCameraMatrix();
}

void scene_references::onPointer(const a::pointer_args& args, void* context)
{
    BAJA_VERIFY(context);
    bool* handled = static_cast<bool*>(context);

    if (_state == state::changingLightRotation) 
    {
        if (args.type == a::pointer_event_type::down)
        {
            // snap current mouse point and camera angles
            _down = true;
            _startPoint = args.point;
            _startAngles = _sceneState->getCamera()->angles();
        }
        else if (args.type == a::pointer_event_type::move && _down)
        {
            m::vector3d angles = _startAngles;

            angles.x -= -0.25f * (args.point.y - _startPoint.y);
            angles.y -= -0.25f * (args.point.x - _startPoint.x);

            _sceneState->getLight()->camera()->setAngles(angles);
        }

        *handled = true;
    }
    else if (_state == state::changingRotation) 
    {
        if (args.type == a::pointer_event_type::down)
        {
            // snap current mouse point and camera angles
            _down = true;
            _startPoint = args.point;
            _startAngles = _sceneState->getCamera()->angles();
        }
        else if (args.type == a::pointer_event_type::move && _down)
        {
            m::vector3d angles = _startAngles;

            angles.x -= 0.25f * (args.point.y - _startPoint.y);
            angles.y -= -0.25f * (args.point.x - _startPoint.x);

            _sceneState->getCamera()->setAngles(angles);
        }

        *handled = true;
    }
    else if (_state == state::changingPosition) 
    {
        if (args.type == a::pointer_event_type::down)
        {
            // snap current mouse point and camera angles
            _down = true;
            _startPoint = args.point;
            _startPosition = _sceneState->getCamera()->offsetXY();
        }
        else if (args.type == a::pointer_event_type::move && _down)
        {
            m::vector2d position = _startPosition;

            position.x += -0.25f * (args.point.x - _startPoint.x);
            position.y += 0.25f * (args.point.y - _startPoint.y);

            _sceneState->getCamera()->setOffsetXY(position);
        }

        *handled = true;
    }
    else if (_state == state::changingDistance) 
    {
        if (args.type == a::pointer_event_type::down)
        {
            // snap current mouse point and camera angles
            _down = true;
            _startPoint = args.point;
            _startDistance = _sceneState->getCamera()->offsetDistance();
        }
        else if (args.type == a::pointer_event_type::move && _down)
        {
            b::float32 distance = _startDistance;

            distance += 0.25f * (args.point.y - _startPoint.y);

            _sceneState->getCamera()->setOffsetDistance(distance);
        }

        *handled = true;
    }
    else if (_state == state::changingFocus) 
    {
        if (args.type == a::pointer_event_type::down)
        {
            // snap current mouse point and camera angles
            _down = true;
            _startPoint = args.point;
            _startFocus = _sceneState->getCamera()->focus();
        }
        else if (args.type == a::pointer_event_type::move && _down)
        {
            m::vector2d focus = _startFocus;

            focus.x += 0.25f * (args.point.y - _startPoint.y);
            focus.y += -0.25f * (args.point.x - _startPoint.x);

            focus.x = max(focus.x, 1.f);
            focus.y = max(focus.y, 1.f);

            _sceneState->getCamera()->setFocus(focus);
        }

        *handled = true;
    }
}

void scene_references::onKey(const a::key_args& args, void* context)
{
    BAJA_VERIFY(context);
    bool* handled = static_cast<bool*>(context);

    state newState = state::none;
    if (journal::app::key_registry::check(journal::app::key::showDisclaimer, args.vk))
    {   
        _renderDisclaimer = !_renderDisclaimer;
    }
    else if (journal::app::key_registry::check(journal::app::key::cameraModeOff, args.vk))
    {   
        newState = state::idle; 
        DEBUG_CONSOLE_TRACE(L"camera mode disabled"); 
    }
    else if (journal::app::key_registry::check(journal::app::key::cameraModeRotate, args.vk))
    { 
        newState = state::changingRotation; 
        DEBUG_CONSOLE_TRACE(L"changing camera rotation"); 
    }
    else if (journal::app::key_registry::check(journal::app::key::cameraModeRotateLight, args.vk))
    { 
        newState = state::changingLightRotation; 
        DEBUG_CONSOLE_TRACE(L"changing light rotation"); 
    }
    else if (journal::app::key_registry::check(journal::app::key::cameraModeZoom, args.vk))
    { 
        newState = state::changingDistance; 
        DEBUG_CONSOLE_TRACE(L"changing camera distance"); 
    } 
    else if (journal::app::key_registry::check(journal::app::key::cameraModeFocus, args.vk))
    { 
        newState = state::changingFocus; 
        DEBUG_CONSOLE_TRACE(L"changing camera focus"); 
    }
    else if (journal::app::key_registry::check(journal::app::key::cameraModeMove, args.vk))
    { 
        newState = state::changingPosition; 
        DEBUG_CONSOLE_TRACE(L"changing camera position"); 
    }
    else 
    {
        return; // nothing to handle
    }

    if (newState != state::none)
    {
        if (newState == state::idle)
        {
            if (_state != state::idle)
            {
                _state = state::idle;
                _inputCapture.lock()->releaseCapture();
            }
        }
        else
        {
            _down = false;
            _state = newState;
            _inputCapture.lock()->setCapture(std::dynamic_pointer_cast<a::iinput_observer>(shared_from_this()));
        }
    }

    *handled = true;
}

void scene_references::updateDebugString()
{
    boost::wformat fmt = 
        boost::wformat(L"pos: (%0.2f, %0.2f), dis: %0.2f, rot: (%0.2f, %0.2f, %0.2f), foc: (%0.2f, %0.2f)") %
            _sceneState->getCamera()->offsetXY().x %
            _sceneState->getCamera()->offsetXY().y %
            _sceneState->getCamera()->offsetDistance() %
            _sceneState->getCamera()->angles().x %
            _sceneState->getCamera()->angles().y %
            _sceneState->getCamera()->angles().z %
            _sceneState->getCamera()->focus().x %
            _sceneState->getCamera()->focus().y;

    std::wstring s = fmt.str();

    std::copy(s.begin(), s.end(), _debugString.begin());
    _debugString[min(s.size(), max_debug_string)] = L'\0';
}

}} // namespace journal::view